package com.zjk.storage.core;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.zjk.storage.core.aspect.FileStorageAspect;
import com.zjk.storage.core.aspect.delete.DeleteAspectChain;
import com.zjk.storage.core.aspect.exist.ExistAspectChain;
import com.zjk.storage.core.aspect.upload.UploadAspectChain;
import com.zjk.storage.core.entity.BaseFileInfo;
import com.zjk.storage.core.entity.CustomMultipartFile;
import com.zjk.storage.core.entity.MultipartFileWrapper;
import com.zjk.storage.core.entity.UploadPretreatment;
import com.zjk.storage.core.exception.FileStorageException;
import com.zjk.storage.core.progress.Downloader;
import com.zjk.storage.core.provider.FileStorage;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;


@NoArgsConstructor
@Setter
@Slf4j
public class FileStorageService {

    private FileStorageService self;
    private FileStorageFactory fileStorageFactory;
    private List<FileStorageAspect> aspects;

    public FileStorageService(FileStorageFactory fileStorageFactory, List<FileStorageAspect> aspects) {
        this.fileStorageFactory = fileStorageFactory;
        this.aspects = aspects;
    }


    public BaseFileInfo upload(UploadPretreatment pre) {
        MultipartFile file = pre.getFileWrapper();
        Assert.isTrue(file != null, "文件不能为空");
        Assert.notEmpty(pre.getPlatform(), "平台不能为空");
        BaseFileInfo fileInfo = new BaseFileInfo()
                .setPlatform(pre.getPlatform())
                .setCreateTime(DateUtil.now())
                .setSize(file.getSize())
                .setOriginalName(file.getOriginalFilename())
                .setExt(FileNameUtil.getSuffix(file.getOriginalFilename()))
                .setPath(pre.getPath());
        try {
            fileInfo.setMd5(DigestUtil.md5Hex(file.getInputStream()));
        } catch (IOException e) {
            log.error("获取文件MD5失败！fileName:{}",fileInfo.getOriginalName(),e);
        }
        if (StrUtil.isNotBlank(pre.getSaveFilename())) {
            fileInfo.setFileName(pre.getSaveFilename());
        } else {
            fileInfo.setFileName(IdUtil.fastSimpleUUID() + Optional.ofNullable(fileInfo.getExt()).map(ext -> StrUtil.DOT + ext).orElse(StrUtil.EMPTY));
        }
        byte[] thumbnailBytes = pre.getThumbnailBytes();
        if (thumbnailBytes != null) {
            fileInfo.setThumbSize((long) thumbnailBytes.length);
            if (StrUtil.isNotEmpty(pre.getSaveThFilename())) {
                fileInfo.setThumbFilename(pre.getSaveThFilename() + pre.getThumbnailSuffix());
            } else {
                fileInfo.setThumbFilename(fileInfo.getFileName() + pre.getThumbnailSuffix());
            }
        }
        FileStorage fileStorage = fileStorageFactory.getFileStorage(pre.getPlatform());

        return new UploadAspectChain(aspects, (_fileInfo, _pre, _fileStorage) -> {
            if (_fileStorage.upload(_fileInfo, _pre)) {
                return _fileInfo;
            }
            return null;
        }).next(fileInfo, pre, fileStorage);
    }

    public boolean delete(BaseFileInfo fileInfo, Predicate<BaseFileInfo> predicate) {
        if (fileInfo == null) {
            return true;
        }
        if (predicate != null && !predicate.test(fileInfo)) {
            return false;
        }
        Assert.notEmpty(fileInfo.getPlatform(), "平台不能为空");

        FileStorage fileStorage = fileStorageFactory.getFileStorage(fileInfo.getPlatform());
        return new DeleteAspectChain(aspects, (_fileInfo, _fileStorage) -> _fileStorage.delete(_fileInfo)).next(fileInfo,fileStorage);
    }

    public boolean exists(BaseFileInfo fileInfo) {
        if (fileInfo == null) {
            return false;
        }
        return new ExistAspectChain(aspects, (_fileInfo, _fileStorage) -> _fileStorage.exists(_fileInfo)).next(fileInfo,fileStorageFactory.getFileStorage(fileInfo.getPlatform()));
    }

    public Downloader download(BaseFileInfo fileInfo) {
        return new Downloader(fileStorageFactory.getFileStorage(fileInfo.getPlatform()),aspects,fileInfo,Downloader.TARGET_FILE);
    }
    public Downloader downloadTh(BaseFileInfo fileInfo) {
        return new Downloader(fileStorageFactory.getFileStorage(fileInfo.getPlatform()),aspects,fileInfo,Downloader.TARGET_THUNB_FILE);
    }

    public UploadPretreatment of() {
        UploadPretreatment pre = new UploadPretreatment();
        pre.setFileStorageService(self);
        return pre;
    }
    public UploadPretreatment of(MultipartFile file) {
        UploadPretreatment pre = of();
        pre.setFileWrapper(new MultipartFileWrapper(file));
        return pre;
    }

    /**
     * 根据 byte[] 创建上传预处理器，name 为空字符串
     */
    public UploadPretreatment of(byte[] bytes) {
        UploadPretreatment pre = of();
        pre.setFileWrapper(new MultipartFileWrapper(new CustomMultipartFile("", bytes)));
        return pre;
    }

    /**
     * 根据 InputStream 创建上传预处理器，originalFilename 为空字符串
     */
    public UploadPretreatment of(InputStream in) {
        try {
            UploadPretreatment pre = of();
            pre.setFileWrapper(new MultipartFileWrapper(new CustomMultipartFile("", in)));
            return pre;
        } catch (Exception e) {
            throw new FileStorageException("根据 InputStream 创建上传预处理器失败！", e);
        }
    }

    /**
     * 根据 File 创建上传预处理器，originalFilename 为 file 的 name
     */
    public UploadPretreatment of(File file) {
        try {
            UploadPretreatment pre = of();
            pre.setFileWrapper(new MultipartFileWrapper(new CustomMultipartFile(file.getName(), file.getName(), null, new FileInputStream(file))));
            return pre;
        } catch (Exception e) {
            throw new FileStorageException("根据 File 创建上传预处理器失败！", e);
        }
    }

    /**
     * 根据 URL 创建上传预处理器，originalFilename 为空字符串
     */
    public UploadPretreatment of(URL url) {
        try {
            UploadPretreatment pre = of();
            pre.setFileWrapper(new MultipartFileWrapper(new CustomMultipartFile("", url.openStream())));
            return pre;
        } catch (Exception e) {
            throw new FileStorageException("根据 URL 创建上传预处理器失败！", e);
        }
    }

    /**
     * 根据 URI 创建上传预处理器，originalFilename 为空字符串
     */
    public UploadPretreatment of(URI uri) {
        try {
            return of(uri.toURL());
        } catch (Exception e) {
            throw new FileStorageException("根据 URI 创建上传预处理器失败！", e);
        }
    }

    /**
     * 根据 url 字符串创建上传预处理器，兼容Spring的ClassPath路径、文件路径、HTTP路径等，originalFilename 为空字符串
     */
    public UploadPretreatment of(String url) {
        try {
            return of(URLUtil.url(url));
        } catch (Exception e) {
            throw new FileStorageException("根据 url：" + url + " 创建上传预处理器失败！", e);
        }
    }


}
